package org.raymond.iworks.study.spring.controller;

import lombok.extern.slf4j.Slf4j;
import org.junit.jupiter.api.Test;
import org.raymond.iworks.StudyApplication;
import org.raymond.iworks.study.spring.service.LockSQLService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

/**
 * @author raymond
 * @version V1.0
 * @Description: TODO
 */
@SpringBootTest(classes= StudyApplication.class)
@Slf4j
public class LockTest {
    @Autowired
    private LockSQLService lockSQLService;

    // mysql version: 8.0.18
    // springboot 3.0.5
    // select @@global.transaction_isolation, @@transaction_isolation;
    // REPEATABLE-READ, REPEATABLE-READ
    // set global transaction_isolation='read-uncommitted';
    // set global transaction_isolation='read-committed';
    // set global transaction_isolation='repeatable-read';
    // set global transaction_isolation='serializable';
    // set session binlog_format=row;
    // spring与数据库的隔离级别不一致时,以spring的配置为准
    // 测试条件1:mysql隔离级别默认为repeatable-read不变,binlog_format=row不变,包含三次查询的方法A的spring事务配置为默认, 修改数据的方法B的事务配置为read-committed
    // 结果:方法A第一次查询正常,然后方法B修改数据,方法A第二次查询的是修改前的数据,方法A第三次查询的还是修改前的数据
    // 测试条件2:基于测试条件1,修改方法A的事务级别为repeatable-read
    // 结果:与测试条件1的结果一致
    // 测试条件3:基于测试条件2,去掉方法A的事务配置
    // 结果:方法A第一次查询正常,然后方法B修改数据,方法A第二次查询的是修改后的数据,方法A第三次查询的还是修改后的数据
    // 测试条件4:基于测试条件3,修改方法A的事务级别为read-committed
    // 结果:方法A第一次查询正常,然后方法B修改数据,方法A第二次查询的是修改前的数据,方法A第三次查询的还是修改后的数据
    // 测试条件5:方法B的事务配置改为repeatable-read,其他与测试条件1一致
    // 结果:与测试条件1的结果一致
    // 测试条件6:方法B的事务配置改为repeatable-read,其他与测试条件2一致
    // 结果:与测试条件2的结果一致
    // 测试条件7:方法B的事务配置改为repeatable-read,其他与测试条件3一致
    // 结果:与测试条件3的结果一致
    // 测试条件8:方法B的事务配置改为repeatable-read,其他与测试条件4一致
    // 结果:与测试条件4的结果一致


    // 测试条件1:mysql隔离级别改为read-committed,binlog_format=row不变,包含三次查询的方法A的spring事务配置为默认, 修改数据的方法B的事务配置为read-committed
    // 结果:方法A第一次查询正常,然后方法B修改数据,方法A第二次查询的是修改前的数据,方法A第三次查询的是修改后的数据
    // 测试条件2:基于测试条件1,修改方法A的事务级别为repeatable-read
    // 结果:方法A第一次查询正常,然后方法B修改数据,方法A第二次查询的是修改前的数据,方法A第三次查询的还是修改前的数据
    // 测试条件3:基于测试条件2,去掉方法A的事务配置
    // 结果:方法A第一次查询正常,然后方法B修改数据,方法A第二次查询的是修改后的数据,方法A第三次查询的还是修改后的数据
    // 测试条件4:基于测试条件3,修改方法A的事务级别为read-committed
    // 结果:与测试条件1的结果一致
    // 测试条件5:方法B的事务配置改为repeatable-read,其他与测试条件1一致
    // 结果:与测试条件1的结果一致
    // 测试条件6:方法B的事务配置改为repeatable-read,其他与测试条件2一致
    // 结果:与测试条件2的结果一致
    // 测试条件7:方法B的事务配置改为repeatable-read,其他与测试条件3一致
    // 结果:与测试条件3的结果一致
    // 测试条件8:方法B的事务配置改为repeatable-read,其他与测试条件4一致
    // 结果:与测试条件4的结果一致
    @Test
    public void test01(){
        Thread t1 = new Thread(()->{
            lockSQLService.queryRepeatableRead();
        });
        Thread t2 = new Thread(()->{
            lockSQLService.updateGap();
        });
        t1.start();
        try {
            Thread.sleep(1000*2);
        } catch (InterruptedException e) {
        }
        t2.start();

        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
        }
        lockSQLService.reset();
    }

    // 测试幻读
    // 测试条件1:mysql隔离级别默认为repeatable-read不变,binlog_format=row不变,包含三次查询的方法A的spring事务配置为默认, 修改数据的方法B的事务配置为read-committed
    // 结果:方法A第一次查询是3条数据,然后方法B插入数据,方法A第二次查询还是3,方法A第三次查询还是3
    // 测试条件2:基于测试条件1,修改方法A的事务级别为repeatable-read
    // 结果:与测试条件1的结果一致
    // 测试条件3:基于测试条件2,去掉方法A的事务配置
    // 结果:方法A第一次查询是3条数据,然后方法B插入数据,方法A第二次查询是4,方法A第三次查询还是4
    // 测试条件4:基于测试条件3,修改方法A的事务级别为read-committed
    // 结果:与测试条件1的结果一致

    // 测试条件1:mysql隔离级别改为read-committed,binlog_format=row不变,包含三次查询的方法A的spring事务配置为默认, 修改数据的方法B的事务配置为read-committed
    // 结果:方法A第一次查询是3条数据,然后方法B插入数据,方法A第二次查询还是3,方法A第三次查询还是3
    // 测试条件2:基于测试条件1,修改方法A的事务级别为repeatable-read
    // 结果:与测试条件1的结果一致
    // 测试条件3:基于测试条件2,去掉方法A的事务配置
    // 结果:方法A第一次查询是3条数据,然后方法B插入数据,方法A第二次查询是4,方法A第三次查询还是4
    // 测试条件4:基于测试条件3,修改方法A的事务级别为read-committed
    // 结果:与测试条件1的结果一致
    @Test
    public void test02(){
        Thread t1 = new Thread(()->{
            lockSQLService.queryPhantomRead();
        });
        Thread t2 = new Thread(()->{
            lockSQLService.insertGap();
        });
        t1.start();
        try {
            Thread.sleep(1000*2);
        } catch (InterruptedException e) {
        }
        t2.start();

        try {
            Thread.sleep(1000*10);
        } catch (InterruptedException e) {
        }
        lockSQLService.reset();
    }
}
